package com.bpmn.flow.cmd;

import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author rain
 * @description 流程复活Command类
 * @date 2025年02月17日 17:58
 */
@AllArgsConstructor
public class RestartProcessInstanceCmd implements Command<ProcessInstance> {

    /**
     * 待复活的流程实例编号
     */
    protected String processInstanceId;

    /**
     * 要复活的节点列表
     */
    protected List<String> activityIds;

    @Override
    public ProcessInstance execute(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        //获取历史服务
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        //校验和查询历史流程实例
        HistoricProcessInstance historicProcessInstance = checkAndGetHistoricProcessInstance(historyService);
        //校验流程定义
        ProcessDefinition processDefinition = ProcessDefinitionUtil.getProcessDefinition(historicProcessInstance.getProcessDefinitionId());
        if (processDefinition == null) {
            throw new FlowableException("流程定义不存在");
        }
        //获取流程启动节点
        Process process = ProcessDefinitionUtil.getProcess(processDefinition.getId());
        StartEvent initialFlowElement = (StartEvent) process.getInitialFlowElement();
        //重建运行时流程实例的主执行流程
        ExecutionEntity processInstance = reCreateProcessInstance(processDefinition, initialFlowElement, historicProcessInstance);
        //创建子执行实例
        ExecutionEntity childExecution = CommandContextUtil.getExecutionEntityManager().createChildExecution(processInstance);
        childExecution.setCurrentFlowElement(initialFlowElement);
        //收集待复活的流程变量
        Map<String, Object> varMap = collectVariables(historyService, historicProcessInstance);
        //设置历史流程实例结束节点和结束时间为空
        ((HistoricProcessInstanceEntityImpl) historicProcessInstance).setEndActivityId(null);
        ((HistoricProcessInstanceEntityImpl) historicProcessInstance).setEndTime(null);
        // 执行动态跳转
        processEngineConfiguration.getRuntimeService().createChangeActivityStateBuilder()
                .processInstanceId(processInstance.getProcessInstanceId())
                .moveSingleExecutionToActivityIds(childExecution.getId(), activityIds)
                .processVariables(varMap)
                .changeState();
        return processEngineConfiguration.getRuntimeService().createProcessInstanceQuery()
                .processInstanceId(processInstance.getProcessInstanceId()).singleResult();
    }

    /**
     * 校验和查询历史流程实例
     */
    private HistoricProcessInstance checkAndGetHistoricProcessInstance(HistoryService historyService) {
        if (StrUtil.isBlank(processInstanceId)) {
            throw new FlowableIllegalArgumentException("processInstanceId不能为空!");
        }
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance == null) {
            throw new FlowableException("id为" + processInstanceId + "的流程实例不存在");
        } else if (historicProcessInstance.getEndTime() == null) {
            throw new FlowableException("id为" + processInstanceId + "的流程实例没有结束!");
        }
        return historicProcessInstance;
    }

    /**
     * 重建流程实例
     */
    private ExecutionEntity reCreateProcessInstance(ProcessDefinition processDefinition, StartEvent initialFlowElement, HistoricProcessInstance historicProcessInstance) {
        ExecutionEntity processInstanceExecution = CommandContextUtil.getExecutionEntityManager()
                .createProcessInstanceExecution(processDefinition, historicProcessInstance.getId(),
                        historicProcessInstance.getBusinessKey(), historicProcessInstance.getBusinessStatus(),
                        historicProcessInstance.getName(), historicProcessInstance.getCallbackId(),
                        historicProcessInstance.getCallbackType(), historicProcessInstance.getReferenceId(),
                        historicProcessInstance.getReferenceType(),
                        historicProcessInstance.getPropagatedStageInstanceId(),
                        historicProcessInstance.getTenantId(), initialFlowElement.getInitiator(),
                        historicProcessInstance.getStartActivityId());
        //重设流程开始时间
        processInstanceExecution.setStartTime(historicProcessInstance.getStartTime());
        return processInstanceExecution;
    }

    /**
     * 手机待复活的流程变量
     */
    private Map<String, Object> collectVariables(HistoryService historyService, HistoricProcessInstance historicProcessInstance) {
        Map<String, Object> varMap = new HashMap<>();
        //查询历史流程变量
        List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(historicProcessInstance.getId())
                .executionId(historicProcessInstance.getId()).list();
        for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {
            varMap.put(historicVariableInstance.getVariableName(), historicVariableInstance.getValue());
        }
        return varMap;
    }

}
